{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "77c0f36a",
   "metadata": {
    "cellView": "form",
    "id": "77c0f36a"
   },
   "outputs": [],
   "source": [
    "# @title ###### Licensed to the Apache Software Foundation (ASF), Version 2.0 (the \"License\")\n",
    "\n",
    "# Licensed to the Apache Software Foundation (ASF) under one\n",
    "# or more contributor license agreements. See the NOTICE file\n",
    "# distributed with this work for additional information\n",
    "# regarding copyright ownership. The ASF licenses this file\n",
    "# to you under the Apache License, Version 2.0 (the\n",
    "# \"License\"); you may not use this file except in compliance\n",
    "# with the License. You may obtain a copy of the License at\n",
    "#\n",
    "#   http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing,\n",
    "# software distributed under the License is distributed on an\n",
    "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n",
    "# KIND, either express or implied. See the License for the\n",
    "# specific language governing permissions and limitations\n",
    "# under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3683a5f8",
   "metadata": {
    "id": "3683a5f8"
   },
   "source": [
    "# 🏭 Coal Plant ON/OFF: Predictions\n",
    "\n",
    "* **Time estimate**: 1 hour\n",
    "* **Cost estimate**: Around $**1**.00 USD (_free_ if you use \\$300 [Cloud credits](https://cloud.google.com/free/docs/gcp-free-tier))\n",
    "\n",
    "> [Watch the video in YouTube<br> ![thumbnail](http://img.youtube.com/vi/8amFK7T_n30/0.jpg)](https://youtu.be/8amFK7T_n30)\n",
    ">\n",
    "\n",
    "\n",
    "This is an **interactive** notebook that contains **all** of the **code** necessary to train an ML model from satellite images for geospatial classification of whether a coal plant is on/off. \n",
    "\n",
    "This is a first step **introductory example** of how these **satellite images** can be used to detect **carbon pollution** from power plants.\n",
    "\n",
    "--------------------------------------------\n",
    "\n",
    "\n",
    "💚 This is one of many **machine learning how-to samples** inspired from **real climate solutions** aired on the [People and Planet AI 🎥 series](https://www.youtube.com/playlist?list=PLIivdWyY5sqI-llB35Dcb187ZG155Rs_7)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46b21577",
   "metadata": {
    "id": "46b21577"
   },
   "source": [
    "## 🙈 Using this interactive notebook\n",
    "\n",
    "Click the **run** icons ▶️ of each section within this notebook. \n",
    "\n",
    "This notebook code lets you train and deploy an ML model from end-to-end. When you run a code cell, the code runs in the notebook's runtime, so you're not making any changes to your personal computer.\n",
    "\n",
    "> 🛎️ **To avoid any errors**, wait for each section to finish in their order before clicking the next “run” icon.\n",
    "\n",
    "This sample must be connected to a **Google Cloud project**, but nothing else is needed other than your Google Cloud project.\n",
    "You can use an existing project and the cost will be around **$1.00**. Alternatively, you can create a new Cloud project [with cloud credits for free.](https://cloud.google.com/free/docs/gcp-free-tier)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3547aec6",
   "metadata": {
    "id": "3547aec6"
   },
   "source": [
    "## 🚴‍♀️ Steps summary\n",
    "\n",
    "Here's a quick summary of what you’ll go through:\n",
    "\n",
    "1. **Get the training data** _(~15 minutes to complete, no cost for using Earth Engine)_:\n",
    "  Extract satellite images from [Earth Engine](https://earthengine.google.com/), combine it with the data that was labeled and contains lat/long coordinates from [Climate TRACE](https://climatetrace.org) in a CSV, and export to\n",
    "  [Cloud Storage](https://cloud.google.com/storage).\n",
    "\n",
    "\n",
    "2. **Run a custom training job**  _(~15 minutes to complete, [costs ~ $1](https://cloud.google.com/vertex-ai/pricing#custom-trained_models))_:\n",
    "  Using [Tensorflow](https://www.tensorflow.org/) on [Vertex AI Training](https://cloud.google.com/vertex-ai/docs/training/custom-training) using a [pre-built training container](https://cloud.google.com/vertex-ai/docs/training/pre-built-containers).\n",
    "\n",
    "\n",
    "3. **Deploy a web service to host the trained model** _(~7 minutes to complete, [costs a few cents to build the image](https://cloud.google.com/build/pricing), and [deployment cost covered by free tier](https://cloud.google.com/run/pricing))_:\n",
    "  On\n",
    "  [Cloud Run](https://cloud.google.com/run)\n",
    "  and get predictions using the model.\n",
    "\n",
    "\n",
    "4. **Get Predictions** _(a few seconds per prediction, [costs covered by free tier](https://cloud.google.com/run/pricing))_:\n",
    "  Use the web service to get predictions for new data.\n",
    "\n",
    "\n",
    "5. **Visualize predictions** _(~5 minutes to complete)_ :\n",
    "  Visualize the predictions on a map.\n",
    "\n",
    "\n",
    "6. (Optional) **Delete the project** to avoid ongoing costs."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0033a40",
   "metadata": {
    "id": "f0033a40"
   },
   "source": [
    "## ✨ Before you begin, you need to…\n",
    "\n",
    "1. Decide on creating a new\n",
    "   [free project](https://cloud.google.com/free/docs/gcp-free-tier)\n",
    "   _(recommended)_ or using an existing one.\n",
    "   Then [**copy the project ID**](https://cloud.google.com/resource-manager/docs/creating-managing-projects) and paste it in the `google_cloud_project` field in the \"Entering project details” section below.\n",
    "\n",
    "   > 💡 If you _don't plan to keep the resources_ that you create via this sample, we recommend creating a new project instead of selecting an existing project.\n",
    "   > After you finish these steps, you can delete the project, removing all the resources associated in bulk.\n",
    "   \n",
    "\n",
    "2. [_Click here_](https://console.cloud.google.com/flows/enableapi?apiid=containerregistry,cloudbuild,run,aiplatform,earthengine.googleapis.com)\n",
    "   to **enable the following APIs** in your Google Cloud project:\n",
    "   _Earth Engine_, _Vertex AI_, _Container Registry_, _Cloud Build_, and _Cloud Run_.\n",
    "\n",
    "\n",
    "3. Make sure that **billing is enabled** for your Google Cloud project,\n",
    "   [_click here_](https://cloud.google.com/billing/docs/how-to/modify-project)\n",
    "   to learn how to confirm that billing is enabled.\n",
    "\n",
    "\n",
    "4. [_Click here_](https://console.cloud.google.com/storage/create-bucket)\n",
    "   to create a Cloud Storage bucket.\n",
    "   Then copy the bucket’s name and paste it in the `cloud_storage_bucket` field in the “Entering project details” section below.\n",
    "   \n",
    "   > 🛎️ Make sure it's a _regional_ bucket in a location where\n",
    "   > [Vertex AI is available](https://cloud.google.com/vertex-ai/docs/general/locations#vertex-ai-regions).\n",
    "\n",
    "5. Have an **Earth Engine** account (it's FREE) or create a new one.\n",
    "  To create an account, fill out the [registration form here.](https://signup.earthengine.google.com/#!/). Please note this can take from 0-24 hours...but it's worth it! Come back to this sample after you have this.\n",
    "  \n",
    "  \n",
    "  \n",
    "  "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "156ab72c",
   "metadata": {
    "id": "156ab72c"
   },
   "source": [
    "### ⛏️ Preparing the project environment\n",
    "\n",
    "Click the run ▶️ icons in order for the cells to download and install the necessary code, libraries, and resources for this solution.\n",
    "\n",
    "> 💡 You can _optionally_ view the entire\n",
    "> [code in GitHub](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/people-and-planet-ai/geospatial-classification)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa34fdd8",
   "metadata": {
    "id": "fa34fdd8"
   },
   "source": [
    "### ↘️ Get the code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c2848f6d",
   "metadata": {
    "id": "c2848f6d"
   },
   "outputs": [],
   "source": [
    "# Get the sample source code.\n",
    "\n",
    "!git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git ~/python-docs-samples\n",
    "%cd ~/python-docs-samples/people-and-planet-ai/geospatial-classification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bb8c6ede",
   "metadata": {
    "id": "bb8c6ede"
   },
   "outputs": [],
   "source": [
    "!pip install -r requirements.txt -c constraints.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0-SFNGO63asJ",
   "metadata": {
    "id": "0-SFNGO63asJ"
   },
   "source": [
    "> 🛎️ **\\[DON’T PANIC\\]** It’s safe to _ignore the warnings_.\n",
    "> When we `pip install` the requirements, there might be some warnings about conflicting dependency versions.\n",
    "> For the scope of this sample, that’s ok.\n",
    "\n",
    "> ⚠️ **Restart the runtime**: Running the previous cell just updated some libraries and requires to restart the runtime to load those libraries correctly.\n",
    ">\n",
    "> In the top-left menu, click _**\"Runtime\" > \"Restart runtime\"**_."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "980b439f-03bb-427f-ae61-4d6d5f066d15",
   "metadata": {
    "id": "980b439f-03bb-427f-ae61-4d6d5f066d15"
   },
   "source": [
    "### ✏️ Enter your Cloud project's details. Ensure you provide a regional bucket!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "246702e7",
   "metadata": {
    "cellView": "form",
    "id": "246702e7"
   },
   "outputs": [],
   "source": [
    "# @title My Google Cloud resources\n",
    "project = \"\"  # @param {type:\"string\"}\n",
    "cloud_storage_bucket = \"\"  # @param {type:\"string\"}\n",
    "region = \"\"  # @param {type:\"string\"}\n",
    "\n",
    "# Validate the inputs.\n",
    "if not project:\n",
    "    raise ValueError(f\"Please provide a value for 'project'\")\n",
    "if not cloud_storage_bucket:\n",
    "    raise ValueError(f\"Please provide a value for 'cloud_storage_bucket'\")\n",
    "if not region:\n",
    "    raise ValueError(f\"Please provide a value for 'region'\")\n",
    "\n",
    "# Authenticate\n",
    "from google.colab import auth\n",
    "\n",
    "auth.authenticate_user()\n",
    "print(\"Authenticated\")\n",
    "\n",
    "!gcloud config set project {project}\n",
    "\n",
    "%cd ~/python-docs-samples/people-and-planet-ai/geospatial-classification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ec73dd9",
   "metadata": {
    "id": "4ec73dd9"
   },
   "source": [
    "### 🗺️ Authenticate to Earth Engine\n",
    "\n",
    "In order to use the Earth Engine API, you'll **need** to have an **Earth Engine account**.\n",
    "\n",
    "To create an account, fill out the [registration form here.](https://signup.earthengine.google.com/#!/)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4cb4cd13",
   "metadata": {
    "id": "4cb4cd13"
   },
   "outputs": [],
   "source": [
    "import ee\n",
    "import google.auth\n",
    "\n",
    "credentials, _ = google.auth.default()\n",
    "ee.Initialize(credentials, project=project)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "922968ad",
   "metadata": {
    "id": "922968ad"
   },
   "source": [
    "# 🚏 Overview\n",
    "\n",
    "This notebook leverages geospatial data from [Google Earth Engine](https://earthengine.google.com/), and labeled data provided by the organization [Climate TRACE](https://www.climatetrace.org/). By combining these two data sources, you'll build and train a model that predicts whether or not a power plant is turned on and producing emissions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f15adff",
   "metadata": {
    "id": "8f15adff"
   },
   "source": [
    "### 🛰️ Data _(inputs)_\n",
    "\n",
    "The data in this example consists of images from a satellite called [Sentinel-2](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2#description), a wide-swath, **high-resolution**, multi-spectral imaging mission for land monitoring studies.\n",
    "\n",
    "When working with satellite data, each input image has the **dimensions** `[width, height, bands]`. **Bands** are measurements from specific satellite instruments for different ranges of the **electromagnetic spectrum**. For example, Sentinel-2 contains [🌈 13 spectral bands](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2#bands). If you're familiar with image classification problems, you can think of the bands as similar to an image's RGB (red, green, blue) channels. However, when working with satellite data we generally have **more than just 3** channels.\n",
    "\n",
    "![satellite_inputs](https://github.com/nikitamaia/python-docs-samples/blob/geospatial-sandbox/people-and-planet-ai/geospatial-classification/img/inputs.png?raw=1)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d15be296",
   "metadata": {
    "id": "d15be296"
   },
   "source": [
    "### 🏷️ Labels _(outputs)_\n",
    "\n",
    "For each patch of pixels (an image of a power plant) that we give to the model, it performs **binary classification**, which indicates whether the power plant is on or off.\n",
    "\n",
    "In this example, the **output** is a single number between *0 (Off) and 1 (On)*, representing the **probability** of that power plant being ON."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4dce50bc",
   "metadata": {
    "id": "4dce50bc"
   },
   "source": [
    "### Model _(function)_\n",
    "\n",
    "**TL;DR**\n",
    "*The model will receive a patch of pixels, in the center is the power plant tower. We take 16 pixels as padding creating a 33x33 patch. The model returns a classification of ON/OFF*\n",
    "\n",
    "In this example, we have a CSV file of labels. Each row in this file represents a power plant at a specific lat/lon and timestamp. At training time we'll prepare a dataset where each input image is a single pixel that we have a label for. We will then add padding around that image. These padded pixels will not get predictions, but will help our model to make better predictions for the center point that we have a label for.\n",
    "\n",
    "For example, with a padding of 16, each 1 pixel input point would become a 33x33 image after the padding is added.\n",
    "\n",
    "![training](https://github.com/nikitamaia/python-docs-samples/blob/geospatial-sandbox/people-and-planet-ai/geospatial-classification/img/training.png?raw=1)\n",
    "\n",
    "The model in this sample is trained for image patches where a power plant is located in the center, and the dimensions must be 33x33 pixels where each pixel has a constant number of bands."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "980b0418",
   "metadata": {
    "id": "980b0418"
   },
   "source": [
    "# 1. 🛰️ Get the training data\n",
    "\n",
    "The training data in this sample comes from **two places**: \n",
    "\n",
    "1. The satellite images will be extracted from *Earth Engine*.\n",
    "\n",
    "2. The **labels** are provided in a *CSV file* that indicates whether a *coal plant* is turned *on or off* at a **particular timestamp**. \n",
    "\n",
    "For each row in the CSV file, we need to extract the corresponding Sentinel image taken at that specific latitude/longitude and timestamp. We'll **export** this image data, along with the corresponding label (on/off), to Cloud Storage."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94f60a82",
   "metadata": {
    "id": "94f60a82"
   },
   "outputs": [],
   "source": [
    "# Define constants\n",
    "\n",
    "LABEL = \"is_powered_on\"\n",
    "IMAGE_COLLECTION = \"COPERNICUS/S2\"\n",
    "BANDS = [\n",
    "    \"B1\",\n",
    "    \"B2\",\n",
    "    \"B3\",\n",
    "    \"B4\",\n",
    "    \"B5\",\n",
    "    \"B6\",\n",
    "    \"B7\",\n",
    "    \"B8\",\n",
    "    \"B8A\",\n",
    "    \"B9\",\n",
    "    \"B10\",\n",
    "    \"B11\",\n",
    "    \"B12\",\n",
    "]\n",
    "SCALE = 10\n",
    "PATCH_SIZE = 16"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beba81d1",
   "metadata": {
    "id": "beba81d1"
   },
   "source": [
    "### 🏷️ Import labels\n",
    "\n",
    "First, we **import** the **CSV** file that contains the labels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "42190dcf",
   "metadata": {
    "id": "42190dcf"
   },
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "labels_dataframe = pd.read_csv(\"labeled_geospatial_data.csv\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cdb7d16-a9c7-4be8-b403-ccb10dcad261",
   "metadata": {
    "id": "1cdb7d16-a9c7-4be8-b403-ccb10dcad261"
   },
   "source": [
    "Each row in this dataframe represents a power plant at a particular timestamp. \n",
    "\n",
    "The \"is_powered_on\" column indicates whether or not the coal plant was turned **on (1)** or **off (0)** at that timestamp."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe94b4d3",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 206
    },
    "id": "fe94b4d3",
    "outputId": "d9c2d364-e4a7-46ad-fa8d-ed2e9d8af3e9"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "  <div id=\"df-b9af3cf7-e477-480d-a579-d7304a19f9cf\">\n",
       "    <div class=\"colab-df-container\">\n",
       "      <div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>timestamp</th>\n",
       "      <th>lat</th>\n",
       "      <th>lon</th>\n",
       "      <th>is_powered_on</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>2020-07-03 16:32:41.397000+00:00</td>\n",
       "      <td>39.11613</td>\n",
       "      <td>-84.80529</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2018-06-09 16:25:19.280000+00:00</td>\n",
       "      <td>39.11613</td>\n",
       "      <td>-84.80529</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2017-11-24 16:36:14.460000+00:00</td>\n",
       "      <td>39.11613</td>\n",
       "      <td>-84.80529</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2019-11-01 16:32:42.327000+00:00</td>\n",
       "      <td>39.11613</td>\n",
       "      <td>-84.80529</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2020-05-09 16:32:43.614000+00:00</td>\n",
       "      <td>39.11613</td>\n",
       "      <td>-84.80529</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>\n",
       "      <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-b9af3cf7-e477-480d-a579-d7304a19f9cf')\"\n",
       "              title=\"Convert this dataframe to an interactive table.\"\n",
       "              style=\"display:none;\">\n",
       "        \n",
       "  <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
       "       width=\"24px\">\n",
       "    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n",
       "    <path d=\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\"/><path d=\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\"/>\n",
       "  </svg>\n",
       "      </button>\n",
       "      \n",
       "  <style>\n",
       "    .colab-df-container {\n",
       "      display:flex;\n",
       "      flex-wrap:wrap;\n",
       "      gap: 12px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert {\n",
       "      background-color: #E8F0FE;\n",
       "      border: none;\n",
       "      border-radius: 50%;\n",
       "      cursor: pointer;\n",
       "      display: none;\n",
       "      fill: #1967D2;\n",
       "      height: 32px;\n",
       "      padding: 0 0 0 0;\n",
       "      width: 32px;\n",
       "    }\n",
       "\n",
       "    .colab-df-convert:hover {\n",
       "      background-color: #E2EBFA;\n",
       "      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
       "      fill: #174EA6;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert {\n",
       "      background-color: #3B4455;\n",
       "      fill: #D2E3FC;\n",
       "    }\n",
       "\n",
       "    [theme=dark] .colab-df-convert:hover {\n",
       "      background-color: #434B5C;\n",
       "      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
       "      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
       "      fill: #FFFFFF;\n",
       "    }\n",
       "  </style>\n",
       "\n",
       "      <script>\n",
       "        const buttonEl =\n",
       "          document.querySelector('#df-b9af3cf7-e477-480d-a579-d7304a19f9cf button.colab-df-convert');\n",
       "        buttonEl.style.display =\n",
       "          google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
       "\n",
       "        async function convertToInteractive(key) {\n",
       "          const element = document.querySelector('#df-b9af3cf7-e477-480d-a579-d7304a19f9cf');\n",
       "          const dataTable =\n",
       "            await google.colab.kernel.invokeFunction('convertToInteractive',\n",
       "                                                     [key], {});\n",
       "          if (!dataTable) return;\n",
       "\n",
       "          const docLinkHtml = 'Like what you see? Visit the ' +\n",
       "            '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
       "            + ' to learn more about interactive tables.';\n",
       "          element.innerHTML = '';\n",
       "          dataTable['output_type'] = 'display_data';\n",
       "          await google.colab.output.renderOutput(dataTable, element);\n",
       "          const docLink = document.createElement('div');\n",
       "          docLink.innerHTML = docLinkHtml;\n",
       "          element.appendChild(docLink);\n",
       "        }\n",
       "      </script>\n",
       "    </div>\n",
       "  </div>\n",
       "  "
      ],
      "text/plain": [
       "                          timestamp       lat       lon  is_powered_on\n",
       "0  2020-07-03 16:32:41.397000+00:00  39.11613 -84.80529              1\n",
       "1  2018-06-09 16:25:19.280000+00:00  39.11613 -84.80529              1\n",
       "2  2017-11-24 16:36:14.460000+00:00  39.11613 -84.80529              0\n",
       "3  2019-11-01 16:32:42.327000+00:00  39.11613 -84.80529              0\n",
       "4  2020-05-09 16:32:43.614000+00:00  39.11613 -84.80529              1"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "labels_dataframe.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e58c0029",
   "metadata": {
    "id": "e58c0029"
   },
   "source": [
    "### 🎛️ Create train/validation splits"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cf438ec",
   "metadata": {
    "id": "1cf438ec"
   },
   "source": [
    "Before we can train an ML model, we need to split this data into training and validation datasets. We will do this by creating two new dataframes with a 70/30 training validation split."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8c5887c",
   "metadata": {
    "id": "e8c5887c"
   },
   "outputs": [],
   "source": [
    "TRAIN_VALIDATION_SPLIT = 0.7\n",
    "\n",
    "train_dataframe = labels_dataframe.sample(\n",
    "    frac=TRAIN_VALIDATION_SPLIT, random_state=200\n",
    ")  # random state is a seed value\n",
    "validation_dataframe = labels_dataframe.drop(train_dataframe.index).sample(frac=1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96967303",
   "metadata": {
    "id": "96967303"
   },
   "source": [
    "### Merge 🏷️ labels + 🛰️ Sentinel image data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa236af4",
   "metadata": {
    "id": "fa236af4"
   },
   "source": [
    "In Earth Engine, an [`ImageCollection`](https://developers.google.com/earth-engine/guides/ic_creating) is a stack or sequence of images. An [`Image`](https://developers.google.com/earth-engine/guides/image_overview) is composed of one or more bands and each band has its own name, data type, scale, mask and projection. The [`Sentinel-2`](https://developers.google.com/earth-engine/guides/ic_creating) dataset is represented as an `ImageCollection`, where each image in the collection is of a specific geographic location at a particular time.\n",
    "\n",
    "In the cell below, we write a function to extract the Sentinel image taken at the specific latitude/longitude and timestamp for each row of our dataframe.\n",
    "\n",
    "We will store all of this information as an Earth Engine [`Feature Collection`](https://developers.google.com/earth-engine/apidocs/ee-featurecollection). In Earth Engine, a [`Feature`](https://developers.google.com/earth-engine/guides/features) is an object with a _geometry property_ storing a [`Geometry`](https://developers.google.com/earth-engine/guides/geometries) object, and a _properties property_ storing a dictionary of other properties. Groups of related `Features` can be combined into a `FeatureCollection` to enable additional operations on the entire set such as filtering, sorting, and rendering. \n",
    "\n",
    "We first filter the Sentinel-2 `ImageCollection` at the start/end dates for a particular row in our dataframe.\n",
    "\n",
    "Then, using the [`neighorboodToArray`](https://developers.google.com/earth-engine/api_docs#eeimageneighborhoodtoarray) method we create a `FeatureCollection` that contains the satellite data for each band at the latitude and longitude of interest as well as a 16 pixel padding around that point.\n",
    "\n",
    "In the image below you can think of the purple box representing the lat/lon where the power plant is located. And around this pixel, we add the padding.\n",
    "\n",
    "![training](https://github.com/nikitamaia/python-docs-samples/blob/geospatial-sandbox/people-and-planet-ai/geospatial-classification/img/training.png?raw=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a8b42dd",
   "metadata": {
    "id": "3a8b42dd"
   },
   "outputs": [],
   "source": [
    "from datetime import datetime, timedelta\n",
    "\n",
    "\n",
    "def labeled_feature(row):\n",
    "    start = datetime.fromisoformat(row.timestamp)\n",
    "    end = start + timedelta(days=1)\n",
    "    image = (\n",
    "        ee.ImageCollection(IMAGE_COLLECTION)\n",
    "        .filterDate(start.strftime(\"%Y-%m-%d\"), end.strftime(\"%Y-%m-%d\"))\n",
    "        .select(BANDS)\n",
    "        .mosaic()\n",
    "    )\n",
    "    point = ee.Feature(\n",
    "        ee.Geometry.Point([row.lon, row.lat]),\n",
    "        {LABEL: row.is_powered_on},\n",
    "    )\n",
    "    return (\n",
    "        image.neighborhoodToArray(ee.Kernel.square(PATCH_SIZE))\n",
    "        .sampleRegions(ee.FeatureCollection([point]), scale=SCALE)\n",
    "        .first()\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23a1ad4e",
   "metadata": {
    "id": "23a1ad4e"
   },
   "outputs": [],
   "source": [
    "train_features = [labeled_feature(row) for row in train_dataframe.itertuples()]\n",
    "validation_features = [\n",
    "    labeled_feature(row) for row in validation_dataframe.itertuples()\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21267b05-38cc-4f14-ab09-197baef26713",
   "metadata": {
    "id": "21267b05-38cc-4f14-ab09-197baef26713"
   },
   "source": [
    "To get a better sense of what's going on, let's look at the properties for the first `Feature` in the `train_features` list. You can see that it contains a property for the label `is_powered_on`, and 13 additional properies, one for each spectral band."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "994b9f9a",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "994b9f9a",
    "outputId": "3dc76f9b-f447-469e-c9ab-26297c0edc6c"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['system:index',\n",
       " 'is_powered_on',\n",
       " 'B10',\n",
       " 'B11',\n",
       " 'B12',\n",
       " 'B8A',\n",
       " 'B1',\n",
       " 'B2',\n",
       " 'B3',\n",
       " 'B4',\n",
       " 'B5',\n",
       " 'B6',\n",
       " 'B7',\n",
       " 'B8',\n",
       " 'B9']"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ee.FeatureCollection(train_features[0]).propertyNames().getInfo()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c79c514-ad63-4b85-8da2-ab8967fe8d5a",
   "metadata": {
    "id": "8c79c514-ad63-4b85-8da2-ab8967fe8d5a"
   },
   "source": [
    "The data contained in each band property is an array of shape 33x33.\n",
    "\n",
    "For example, here is the data for band B1 in the first element in our list expressed as a numpy array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a70c127e-7c45-4eab-914d-17a0c3a9e42c",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "a70c127e-7c45-4eab-914d-17a0c3a9e42c",
    "outputId": "04d79e2b-cfe4-4760-a33c-b073bd07c82e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1390 1390 1390 ... 1650 1650 1650]\n",
      " [1390 1390 1390 ... 1650 1650 1650]\n",
      " [1307 1307 1307 ... 1655 1655 1655]\n",
      " ...\n",
      " [1395 1395 1395 ... 1369 1369 1387]\n",
      " [1395 1395 1395 ... 1369 1369 1387]\n",
      " [1350 1350 1350 ... 1436 1436 1460]]\n",
      "shape: (33, 33)\n"
     ]
    }
   ],
   "source": [
    "example_feature = np.array(train_features[0].get(\"B1\").getInfo())\n",
    "print(example_feature)\n",
    "print(\"shape: \" + str(example_feature.shape))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c02607c8",
   "metadata": {
    "id": "c02607c8"
   },
   "source": [
    "### 💾 Export data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10c87eb9",
   "metadata": {
    "id": "10c87eb9"
   },
   "source": [
    "Lastly, we'll export the data to a Cloud Storage bucket. We'll export the data as [TFRecords](https://www.tensorflow.org/tutorials/load_data/tfrecord).\n",
    "\n",
    "Later when we run the training job, we'll parse these TFRecords and feed them to the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53f2d867",
   "metadata": {
    "id": "53f2d867"
   },
   "outputs": [],
   "source": [
    "# Export data\n",
    "\n",
    "training_task = ee.batch.Export.table.toCloudStorage(\n",
    "    collection=ee.FeatureCollection(train_features),\n",
    "    description=\"Training image export\",\n",
    "    bucket=cloud_storage_bucket,\n",
    "    fileNamePrefix=\"geospatial_training\",\n",
    "    selectors=BANDS + [LABEL],\n",
    "    fileFormat=\"TFRecord\",\n",
    ")\n",
    "\n",
    "training_task.start()\n",
    "\n",
    "validation_task = ee.batch.Export.table.toCloudStorage(\n",
    "    collection=ee.FeatureCollection(validation_features),\n",
    "    description=\"Validation image export\",\n",
    "    bucket=cloud_storage_bucket,\n",
    "    fileNamePrefix=\"geospatial_validation\",\n",
    "    selectors=BANDS + [LABEL],\n",
    "    fileFormat=\"TFRecord\",\n",
    ")\n",
    "\n",
    "validation_task.start()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dadeab16",
   "metadata": {
    "id": "dadeab16"
   },
   "source": [
    "This export will take around 10 minutes. You can monitor the progress with the following command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d111251",
   "metadata": {
    "id": "4d111251"
   },
   "outputs": [],
   "source": [
    "from pprint import pprint\n",
    "\n",
    "pprint(ee.batch.Task.list())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02b1b9dd",
   "metadata": {
    "id": "02b1b9dd"
   },
   "source": [
    "# 2. 👟 Run a custom training job\n",
    "\n",
    "Once the export jobs have finished, we're **ready to use** that data to train a model on Vertex AI Training.\n",
    "\n",
    "The complete training code can be found in the `task.py` file.\n",
    "\n",
    "To run our custom training job on Vertex AI Training, we'll use the [pre-built containers](https://cloud.google.com/vertex-ai/docs/training/pre-built-containers) provided by Vertex AI to run our training script.\n",
    "\n",
    "We'll also make use of a GPU. Our model training will only take a couple of minutes, so using a GPU isn't really necessary. But for demonstration purposes (since adding a GPU is simple!) we will make sure we use a container image that is GPU compatible, and then add the `accelerator_type` and `accelerator_count` parameters to `job.run`. TensorFlow will make use of a single GPU out of the box without any extra code changes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4b99054-b843-485e-9cf0-c9f8ae739d1c",
   "metadata": {
    "id": "a4b99054-b843-485e-9cf0-c9f8ae739d1c"
   },
   "outputs": [],
   "source": [
    "from google.cloud import aiplatform\n",
    "\n",
    "aiplatform.init(project=project, staging_bucket=cloud_storage_bucket)\n",
    "\n",
    "job = aiplatform.CustomTrainingJob(\n",
    "    display_name=\"geospatial_model_training\",\n",
    "    script_path=\"task.py\",\n",
    "    container_uri=\"us-docker.pkg.dev/vertex-ai/training/tf-gpu.2-7:latest\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4baff79-0347-4452-afaa-9c042472f837",
   "metadata": {
    "id": "a4baff79-0347-4452-afaa-9c042472f837"
   },
   "source": [
    "The job will take around 10 minutes to run."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5893ce1d-cdbe-43fa-98cc-221a7a2530d3",
   "metadata": {
    "id": "5893ce1d-cdbe-43fa-98cc-221a7a2530d3"
   },
   "outputs": [],
   "source": [
    "model = job.run(\n",
    "    accelerator_type=\"NVIDIA_TESLA_T4\",\n",
    "    accelerator_count=1,\n",
    "    args=[f\"--bucket={cloud_storage_bucket}\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c75499ed",
   "metadata": {
    "id": "c75499ed"
   },
   "source": [
    "# 3. 💻 Deploy a web service to host the trained model"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86cbab39",
   "metadata": {
    "id": "86cbab39"
   },
   "source": [
    "Next, we use\n",
    "[Cloud Run](https://cloud.google.com/run)\n",
    "to deploy a web service that exposes a\n",
    "[REST API](https://en.wikipedia.org/wiki/Representational_state_transfer) to\n",
    "get predictions from our trained model.\n",
    "\n",
    "We'll deploy our service to Cloud Run [directly from source code](https://cloud.google.com/run/docs/deploying-source-code) so we don't need to build the container image first. Behind the scenes, this command uses Google Cloud buildpacks and Cloud Build to automatically build a container image from our source code in the `serving_app` directory. To run the web service, we configure Cloud Run to launch\n",
    "[`gunicorn`](https://gunicorn.org)\n",
    "on this container image. \n",
    "\n",
    "Since calls to this web service could launch potentially expensive jobs in our project, we configure it to _only_ accept authenticated calls."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9978056f-4467-4dda-85a5-84655ade9a11",
   "metadata": {
    "id": "9978056f-4467-4dda-85a5-84655ade9a11"
   },
   "source": [
    "## 🐣 Deploy app"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13c299f1-e108-46bd-8b12-06af56a88444",
   "metadata": {
    "id": "13c299f1-e108-46bd-8b12-06af56a88444"
   },
   "outputs": [],
   "source": [
    "# Deploy the web service to Cloud Run.\n",
    "# https://cloud.google.com/sdk/gcloud/reference/run/deploy\n",
    "!gcloud run deploy \"geospatial-service\" \\\n",
    "  --source=serving_app \\\n",
    "  --command=\"gunicorn\" \\\n",
    "  --args=\"--threads=8,--timeout=0,main:app\" \\\n",
    "  --region=\"{region}\" \\\n",
    "  --memory=\"1G\" \\\n",
    "  --no-allow-unauthenticated \\"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a32d4bf-4345-43f7-9371-aa151ad4f521",
   "metadata": {
    "id": "5a32d4bf-4345-43f7-9371-aa151ad4f521"
   },
   "source": [
    "Now we need the web service URL to make calls to the REST API we just exposed. We can use `gcloud run services describe` to get the web service URL.\n",
    "\n",
    "Since we only accept authorized calls in our web service, we also need to authenticate each call.\n",
    "`gcloud` is already authenticated, so we can use `gcloud auth print-identity-token` to get quick access.\n",
    "\n",
    "> ℹ️ For more information on how to do authenticated calls in Cloud Run, see the\n",
    "> [Authentication overview](https://cloud.google.com/run/docs/authenticating/overview) page."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53f5e5e3-d5dd-4cb7-bb38-5b2e3b439ecd",
   "metadata": {
    "id": "53f5e5e3-d5dd-4cb7-bb38-5b2e3b439ecd"
   },
   "outputs": [],
   "source": [
    "import subprocess\n",
    "\n",
    "# Get the web service URL.\n",
    "#   https://cloud.google.com/sdk/gcloud/reference/run/services/describe\n",
    "service_url = (\n",
    "    subprocess.run(\n",
    "        [\n",
    "            \"gcloud\",\n",
    "            \"run\",\n",
    "            \"services\",\n",
    "            \"describe\",\n",
    "            \"geospatial-service\",\n",
    "            f\"--region={region}\",\n",
    "            f\"--format=get(status.url)\",\n",
    "        ],\n",
    "        capture_output=True,\n",
    "    )\n",
    "    .stdout.decode(\"utf-8\")\n",
    "    .strip()\n",
    ")\n",
    "print(f\"service_url: {service_url}\")\n",
    "\n",
    "# Get an identity token for authorized calls to our web service.\n",
    "#   https://cloud.google.com/sdk/gcloud/reference/auth/print-identity-token\n",
    "identity_token = (\n",
    "    subprocess.run(\n",
    "        [\"gcloud\", \"auth\", \"print-identity-token\"],\n",
    "        capture_output=True,\n",
    "    )\n",
    "    .stdout.decode(\"utf-8\")\n",
    "    .strip()\n",
    ")\n",
    "print(f\"identity_token: {identity_token}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa2c4dfe-b55d-4f80-a7eb-9ee86542ce59",
   "metadata": {
    "id": "aa2c4dfe-b55d-4f80-a7eb-9ee86542ce59"
   },
   "source": [
    "Finally, we can test that everything is working.\n",
    "\n",
    "We included a `ping` method in our web service just to *make sure everything is working* as expected.\n",
    "It simply returns back the arguments we passed to the call, as well as a response saying that the call was successful.\n",
    "\n",
    "> 🛎️ This is a convenient way to make sure the web service is reachable, the authentication is working as expected, and the request arguments are passed correctly.\n",
    "\n",
    "We can use Python's\n",
    "[`requests`](https://docs.python-requests.org)\n",
    "library.\n",
    "The web service was built to always accept [JSON](https://www.w3schools.com/whatis/whatis_json.asp)-encoded requests, and returns JSON-encoded responses.\n",
    "\n",
    "For a **request to be successful**, it **must**:\n",
    "\n",
    "* Be an [`HTTP POST`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request\n",
    "* Contain the following **headers**:\n",
    "  * `Authorization: Bearer IDENTITY_TOKEN`\n",
    "  * `Content-Type: application/json`\n",
    "* The data must be **valid JSON**, if *no arguments* are needed we can pass `{}` as an **empty object**.\n",
    "\n",
    "For **ease of use**, `requests.post` has a\n",
    "[`json` parameter](https://docs.python-requests.org/en/master/user/quickstart/#more-complicated-post-requests)\n",
    "that **automatically attaches the header** `Content-Type: application/json` and encodes our data into a JSON string."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9025db3b-d409-46cf-9ce2-6f0209dfbea3",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "9025db3b-d409-46cf-9ce2-6f0209dfbea3",
    "outputId": "977aa6d1-59f6-4dbf-80c7-c9725d43dddc"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'args': 'Hello world!', 'response': 'Your request was successful! 🎉'}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import requests\n",
    "\n",
    "requests.post(\n",
    "    url=f\"{service_url}/ping\",\n",
    "    headers={\"Authorization\": f\"Bearer {identity_token}\"},\n",
    "    json={\"x\": 42, \"message\": \"Hello world!\"},\n",
    ").json()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "45d64a22-887a-4469-9ba3-668ec23f8667",
   "metadata": {
    "id": "45d64a22-887a-4469-9ba3-668ec23f8667"
   },
   "source": [
    "# 4.🔮 Get Predictions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "462b7bf1-cf07-4a93-9cf3-0f9f31c13fa9",
   "metadata": {
    "id": "462b7bf1-cf07-4a93-9cf3-0f9f31c13fa9"
   },
   "source": [
    "Now that we know our app is up and running, we can use it to make predictions.\n",
    "\n",
    "Let's start by making a prediction for a particular coal plant. To do this we will need to extract the Sentinel data from Earth Engine and send it in the body of the post requst to the prediction service.\n",
    "\n",
    "We'll start with a plant located at the coordinates -84.80529, 39.11613, and then extract the satellite data from October 2021."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e477289-398c-4bcb-ab23-aee65ac92388",
   "metadata": {
    "id": "9e477289-398c-4bcb-ab23-aee65ac92388"
   },
   "outputs": [],
   "source": [
    "# Extract image data\n",
    "\n",
    "import json\n",
    "\n",
    "\n",
    "def get_prediction_data(lon, lat, start, end):\n",
    "    \"\"\"Extracts Sentinel image as json at specific lat/lon and timestamp.\"\"\"\n",
    "\n",
    "    location = ee.Feature(ee.Geometry.Point([lon, lat]))\n",
    "    image = (\n",
    "        ee.ImageCollection(IMAGE_COLLECTION)\n",
    "        .filterDate(start, end)\n",
    "        .select(BANDS)\n",
    "        .mosaic()\n",
    "    )\n",
    "\n",
    "    feature = image.neighborhoodToArray(ee.Kernel.square(PATCH_SIZE)).sampleRegions(\n",
    "        collection=ee.FeatureCollection([location]), scale=SCALE\n",
    "    )\n",
    "\n",
    "    return feature.getInfo()[\"features\"][0][\"properties\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a718e67",
   "metadata": {
    "id": "4a718e67"
   },
   "source": [
    "When we call the `get_prediction_data` function  we need to pass in the start and end dates. \n",
    "\n",
    "Sentinel-2 takes pictures every 10 days. At training time, we knew the exact date of the Sentinel-2 image, as this was provided in the labels CSV file. However, for user supplied images for prediction we don't know the specific date the image was taken. To address this, we'll extract data for the entire month of October and then use the `mosaic` function in Earth Engine which will grab the earliest image in that range, stitch together images at the seams, and discard the rest."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86d53ea1-b09e-4afb-8d04-60b73ec59c7c",
   "metadata": {
    "id": "86d53ea1-b09e-4afb-8d04-60b73ec59c7c"
   },
   "outputs": [],
   "source": [
    "prediction_data = get_prediction_data(-84.80529, 39.11613, \"2021-10-01\", \"2021-10-31\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d93aaa8",
   "metadata": {
    "id": "9d93aaa8"
   },
   "source": [
    "The prediction service **expects two things** the **input data** for the prediction as well as the Cloud Storage **path** where the model is stored."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c01631e-5607-45c8-a42e-354fb38a87e1",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5c01631e-5607-45c8-a42e-354fb38a87e1",
    "outputId": "92fd906e-af86-4e1c-f60c-35e84a9831fe"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'predictions': [[[[1.0]]]]}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "requests.post(\n",
    "    url=f\"{service_url}/predict\",\n",
    "    headers={\"Authorization\": f\"Bearer {identity_token}\"},\n",
    "    json={\"data\": prediction_data, \"bucket\": cloud_storage_bucket},\n",
    ").json()[\"predictions\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2222aaf9",
   "metadata": {
    "id": "2222aaf9"
   },
   "source": [
    "# 4. 🗺️ Visualize predictions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e70fd0a5-a1ae-4cac-91cc-342809d9fb3b",
   "metadata": {
    "id": "e70fd0a5-a1ae-4cac-91cc-342809d9fb3b"
   },
   "source": [
    "Let's visualize the results of a coal plant in Spain. First, we get predictions for the four towers at this power plant."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8f6dc24d",
   "metadata": {
    "id": "8f6dc24d"
   },
   "outputs": [],
   "source": [
    "def get_prediction(lon, lat, start, end):\n",
    "    prediction_data = get_prediction_data(lon, lat, start, end)\n",
    "    result = requests.post(\n",
    "        url=f\"{service_url}/predict\",\n",
    "        headers={\"Authorization\": f\"Bearer {identity_token}\"},\n",
    "        json={\"data\": prediction_data, \"bucket\": cloud_storage_bucket},\n",
    "    ).json()\n",
    "    return result[\"predictions\"][\"predictions\"][0][0][0][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9fc56972-47bb-4b37-b447-400cbea5e90a",
   "metadata": {
    "id": "9fc56972-47bb-4b37-b447-400cbea5e90a"
   },
   "outputs": [],
   "source": [
    "lons = [-7.86444, -7.86376, -7.85755, -7.85587]\n",
    "lats = [43.43717, 43.43827, 43.44075, 43.44114]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5230f05e",
   "metadata": {
    "id": "5230f05e"
   },
   "outputs": [],
   "source": [
    "plant_predictions = [\n",
    "    get_prediction(lon, lat, \"2021-10-01\", \"2021-10-31\") for lon, lat in zip(lons, lats)\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be593a27-71b1-4ea1-8c2e-100a0a82ccdd",
   "metadata": {
    "id": "be593a27-71b1-4ea1-8c2e-100a0a82ccdd"
   },
   "source": [
    "Next, we can **plot** these points on a map. **Blue** means our model predicts that the towers are **\"off\"**, and **red** means our model predicts that the towers are **\"on\"** and producing carbon pollution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15ba038b-49c9-40bf-8d87-af7d253d270d",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 771
    },
    "id": "15ba038b-49c9-40bf-8d87-af7d253d270d",
    "outputId": "76e05dd2-3bae-433f-9f34-4df229f7620e"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div style=\"width:100%;\"><div style=\"position:relative;width:100%;height:0;padding-bottom:60%;\"><span style=\"color:#565656\">Make this Notebook Trusted to load map: File -> Trust Notebook</span><iframe src=\"about:blank\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" data-html=%3C%21DOCTYPE%20html%3E%0A%3Chead%3E%20%20%20%20%0A%20%20%20%20%3Cmeta%20http-equiv%3D%22content-type%22%20content%3D%22text/html%3B%20charset%3DUTF-8%22%20/%3E%0A%20%20%20%20%3Cscript%3EL_PREFER_CANVAS%3Dfalse%3B%20L_NO_TOUCH%3Dfalse%3B%20L_DISABLE_3D%3Dfalse%3B%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.4.0/dist/leaflet.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//code.jquery.com/jquery-1.12.4.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js%22%3E%3C/script%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js%22%3E%3C/script%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdn.jsdelivr.net/npm/leaflet%401.4.0/dist/leaflet.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.css%22/%3E%0A%20%20%20%20%3Clink%20rel%3D%22stylesheet%22%20href%3D%22https%3A//rawcdn.githack.com/python-visualization/folium/master/folium/templates/leaflet.awesome.rotate.css%22/%3E%0A%20%20%20%20%3Cstyle%3Ehtml%2C%20body%20%7Bwidth%3A%20100%25%3Bheight%3A%20100%25%3Bmargin%3A%200%3Bpadding%3A%200%3B%7D%3C/style%3E%0A%20%20%20%20%3Cstyle%3E%23map%20%7Bposition%3Aabsolute%3Btop%3A0%3Bbottom%3A0%3Bright%3A0%3Bleft%3A0%3B%7D%3C/style%3E%0A%20%20%20%20%0A%20%20%20%20%3Cmeta%20name%3D%22viewport%22%20content%3D%22width%3Ddevice-width%2C%0A%20%20%20%20%20%20%20%20initial-scale%3D1.0%2C%20maximum-scale%3D1.0%2C%20user-scalable%3Dno%22%20/%3E%0A%20%20%20%20%3Cstyle%3E%23map_557bdd16e7d346f8b4b957e7aed05ba4%20%7B%0A%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20width%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20height%3A%20100.0%25%3B%0A%20%20%20%20%20%20%20%20left%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20top%3A%200.0%25%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C/style%3E%0A%20%20%20%20%3Cscript%20src%3D%22https%3A//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js%22%3E%3C/script%3E%0A%3C/head%3E%0A%3Cbody%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%3Cdiv%20class%3D%22folium-map%22%20id%3D%22map_557bdd16e7d346f8b4b957e7aed05ba4%22%20%3E%3C/div%3E%0A%3C/body%3E%0A%3Cscript%3E%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20var%20bounds%20%3D%20null%3B%0A%20%20%20%20%0A%0A%20%20%20%20var%20map_557bdd16e7d346f8b4b957e7aed05ba4%20%3D%20L.map%28%0A%20%20%20%20%20%20%20%20%27map_557bdd16e7d346f8b4b957e7aed05ba4%27%2C%20%7B%0A%20%20%20%20%20%20%20%20center%3A%20%5B43.44%2C%20-7.86%5D%2C%0A%20%20%20%20%20%20%20%20zoom%3A%2016%2C%0A%20%20%20%20%20%20%20%20maxBounds%3A%20bounds%2C%0A%20%20%20%20%20%20%20%20layers%3A%20%5B%5D%2C%0A%20%20%20%20%20%20%20%20worldCopyJump%3A%20false%2C%0A%20%20%20%20%20%20%20%20crs%3A%20L.CRS.EPSG3857%2C%0A%20%20%20%20%20%20%20%20zoomControl%3A%20true%2C%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%0A%0A%20%20%20%20%0A%20%20%20%20var%20tile_layer_dc5f1fde0f91463385b7057f07c09c67%20%3D%20L.tileLayer%28%0A%20%20%20%20%20%20%20%20%27https%3A//server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/%7Bz%7D/%7By%7D/%7Bx%7D%27%2C%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22attribution%22%3A%20%22ESRI%22%2C%0A%20%20%20%20%20%20%20%20%22detectRetina%22%3A%20false%2C%0A%20%20%20%20%20%20%20%20%22maxNativeZoom%22%3A%2018%2C%0A%20%20%20%20%20%20%20%20%22maxZoom%22%3A%2018%2C%0A%20%20%20%20%20%20%20%20%22minZoom%22%3A%200%2C%0A%20%20%20%20%20%20%20%20%22noWrap%22%3A%20false%2C%0A%20%20%20%20%20%20%20%20%22opacity%22%3A%201%2C%0A%20%20%20%20%20%20%20%20%22subdomains%22%3A%20%22abc%22%2C%0A%20%20%20%20%20%20%20%20%22tms%22%3A%20false%0A%7D%29.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20circle_2116a03d9e854054a33cf017deba7d41%20%3D%20L.circle%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B43.43717%2C%20-7.86444%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%22bubblingMouseEvents%22%3A%20true%2C%0A%20%20%22color%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22dashArray%22%3A%20null%2C%0A%20%20%22dashOffset%22%3A%20null%2C%0A%20%20%22fill%22%3A%20true%2C%0A%20%20%22fillColor%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22fillOpacity%22%3A%200.2%2C%0A%20%20%22fillRule%22%3A%20%22evenodd%22%2C%0A%20%20%22lineCap%22%3A%20%22round%22%2C%0A%20%20%22lineJoin%22%3A%20%22round%22%2C%0A%20%20%22opacity%22%3A%201.0%2C%0A%20%20%22radius%22%3A%2020%2C%0A%20%20%22stroke%22%3A%20true%2C%0A%20%20%22weight%22%3A%203%0A%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20circle_6f8a17ff2fdf4c6b8224f78a7753bfc9%20%3D%20L.circle%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B43.43827%2C%20-7.86376%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%22bubblingMouseEvents%22%3A%20true%2C%0A%20%20%22color%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22dashArray%22%3A%20null%2C%0A%20%20%22dashOffset%22%3A%20null%2C%0A%20%20%22fill%22%3A%20true%2C%0A%20%20%22fillColor%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22fillOpacity%22%3A%200.2%2C%0A%20%20%22fillRule%22%3A%20%22evenodd%22%2C%0A%20%20%22lineCap%22%3A%20%22round%22%2C%0A%20%20%22lineJoin%22%3A%20%22round%22%2C%0A%20%20%22opacity%22%3A%201.0%2C%0A%20%20%22radius%22%3A%2020%2C%0A%20%20%22stroke%22%3A%20true%2C%0A%20%20%22weight%22%3A%203%0A%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20circle_d647e893b1064e50b046e086c8b10271%20%3D%20L.circle%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B43.44075%2C%20-7.85755%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%22bubblingMouseEvents%22%3A%20true%2C%0A%20%20%22color%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22dashArray%22%3A%20null%2C%0A%20%20%22dashOffset%22%3A%20null%2C%0A%20%20%22fill%22%3A%20true%2C%0A%20%20%22fillColor%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22fillOpacity%22%3A%200.2%2C%0A%20%20%22fillRule%22%3A%20%22evenodd%22%2C%0A%20%20%22lineCap%22%3A%20%22round%22%2C%0A%20%20%22lineJoin%22%3A%20%22round%22%2C%0A%20%20%22opacity%22%3A%201.0%2C%0A%20%20%22radius%22%3A%2020%2C%0A%20%20%22stroke%22%3A%20true%2C%0A%20%20%22weight%22%3A%203%0A%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20circle_bbe305fcca0a45089392aeb3c4bbe970%20%3D%20L.circle%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B43.44114%2C%20-7.85587%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%22bubblingMouseEvents%22%3A%20true%2C%0A%20%20%22color%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22dashArray%22%3A%20null%2C%0A%20%20%22dashOffset%22%3A%20null%2C%0A%20%20%22fill%22%3A%20true%2C%0A%20%20%22fillColor%22%3A%20%22%23ff0000ff%22%2C%0A%20%20%22fillOpacity%22%3A%200.2%2C%0A%20%20%22fillRule%22%3A%20%22evenodd%22%2C%0A%20%20%22lineCap%22%3A%20%22round%22%2C%0A%20%20%22lineJoin%22%3A%20%22round%22%2C%0A%20%20%22opacity%22%3A%201.0%2C%0A%20%20%22radius%22%3A%2020%2C%0A%20%20%22stroke%22%3A%20true%2C%0A%20%20%22weight%22%3A%203%0A%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20var%20color_map_0c76c012401b474fb893e7b3e7412d90%20%3D%20%7B%7D%3B%0A%0A%20%20%20%20%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.color%20%3D%20d3.scale.threshold%28%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20.domain%28%5B0.0%2C%200.002004008016032064%2C%200.004008016032064128%2C%200.006012024048096192%2C%200.008016032064128256%2C%200.01002004008016032%2C%200.012024048096192385%2C%200.014028056112224449%2C%200.01603206412825651%2C%200.018036072144288578%2C%200.02004008016032064%2C%200.022044088176352707%2C%200.02404809619238477%2C%200.026052104208416832%2C%200.028056112224448898%2C%200.03006012024048096%2C%200.03206412825651302%2C%200.03406813627254509%2C%200.036072144288577156%2C%200.03807615230460922%2C%200.04008016032064128%2C%200.04208416833667335%2C%200.04408817635270541%2C%200.04609218436873747%2C%200.04809619238476954%2C%200.050100200400801605%2C%200.052104208416833664%2C%200.05410821643286573%2C%200.056112224448897796%2C%200.05811623246492986%2C%200.06012024048096192%2C%200.06212424849699399%2C%200.06412825651302605%2C%200.06613226452905811%2C%200.06813627254509018%2C%200.07014028056112225%2C%200.07214428857715431%2C%200.07414829659318638%2C%200.07615230460921844%2C%200.0781563126252505%2C%200.08016032064128256%2C%200.08216432865731463%2C%200.0841683366733467%2C%200.08617234468937876%2C%200.08817635270541083%2C%200.09018036072144289%2C%200.09218436873747494%2C%200.09418837675350701%2C%200.09619238476953908%2C%200.09819639278557114%2C%200.10020040080160321%2C%200.10220440881763528%2C%200.10420841683366733%2C%200.1062124248496994%2C%200.10821643286573146%2C%200.11022044088176353%2C%200.11222444889779559%2C%200.11422845691382766%2C%200.11623246492985972%2C%200.11823647294589178%2C%200.12024048096192384%2C%200.12224448897795591%2C%200.12424849699398798%2C%200.12625250501002003%2C%200.1282565130260521%2C%200.13026052104208416%2C%200.13226452905811623%2C%200.1342685370741483%2C%200.13627254509018036%2C%200.13827655310621242%2C%200.1402805611222445%2C%200.14228456913827656%2C%200.14428857715430862%2C%200.1462925851703407%2C%200.14829659318637275%2C%200.15030060120240482%2C%200.1523046092184369%2C%200.15430861723446893%2C%200.156312625250501%2C%200.15831663326653306%2C%200.16032064128256512%2C%200.1623246492985972%2C%200.16432865731462926%2C%200.16633266533066132%2C%200.1683366733466934%2C%200.17034068136272545%2C%200.17234468937875752%2C%200.1743486973947896%2C%200.17635270541082165%2C%200.17835671342685372%2C%200.18036072144288579%2C%200.18236472945891782%2C%200.1843687374749499%2C%200.18637274549098196%2C%200.18837675350701402%2C%200.1903807615230461%2C%200.19238476953907815%2C%200.19438877755511022%2C%200.1963927855711423%2C%200.19839679358717435%2C%200.20040080160320642%2C%200.20240480961923848%2C%200.20440881763527055%2C%200.20641282565130262%2C%200.20841683366733466%2C%200.21042084168336672%2C%200.2124248496993988%2C%200.21442885771543085%2C%200.21643286573146292%2C%200.218436873747495%2C%200.22044088176352705%2C%200.22244488977955912%2C%200.22444889779559118%2C%200.22645290581162325%2C%200.22845691382765532%2C%200.23046092184368738%2C%200.23246492985971945%2C%200.23446893787575152%2C%200.23647294589178355%2C%200.23847695390781562%2C%200.24048096192384769%2C%200.24248496993987975%2C%200.24448897795591182%2C%200.24649298597194388%2C%200.24849699398797595%2C%200.250501002004008%2C%200.25250501002004005%2C%200.2545090180360721%2C%200.2565130260521042%2C%200.25851703406813625%2C%200.2605210420841683%2C%200.2625250501002004%2C%200.26452905811623245%2C%200.2665330661322645%2C%200.2685370741482966%2C%200.27054108216432865%2C%200.2725450901803607%2C%200.2745490981963928%2C%200.27655310621242485%2C%200.2785571142284569%2C%200.280561122244489%2C%200.28256513026052105%2C%200.2845691382765531%2C%200.2865731462925852%2C%200.28857715430861725%2C%200.2905811623246493%2C%200.2925851703406814%2C%200.29458917835671344%2C%200.2965931863727455%2C%200.2985971943887776%2C%200.30060120240480964%2C%200.3026052104208417%2C%200.3046092184368738%2C%200.3066132264529058%2C%200.30861723446893785%2C%200.3106212424849699%2C%200.312625250501002%2C%200.31462925851703405%2C%200.3166332665330661%2C%200.3186372745490982%2C%200.32064128256513025%2C%200.3226452905811623%2C%200.3246492985971944%2C%200.32665330661322645%2C%200.3286573146292585%2C%200.3306613226452906%2C%200.33266533066132264%2C%200.3346693386773547%2C%200.3366733466933868%2C%200.33867735470941884%2C%200.3406813627254509%2C%200.342685370741483%2C%200.34468937875751504%2C%200.3466933867735471%2C%200.3486973947895792%2C%200.35070140280561124%2C%200.3527054108216433%2C%200.35470941883767537%2C%200.35671342685370744%2C%200.3587174348697395%2C%200.36072144288577157%2C%200.3627254509018036%2C%200.36472945891783565%2C%200.3667334669338677%2C%200.3687374749498998%2C%200.37074148296593185%2C%200.3727454909819639%2C%200.374749498997996%2C%200.37675350701402804%2C%200.3787575150300601%2C%200.3807615230460922%2C%200.38276553106212424%2C%200.3847695390781563%2C%200.3867735470941884%2C%200.38877755511022044%2C%200.3907815631262525%2C%200.3927855711422846%2C%200.39478957915831664%2C%200.3967935871743487%2C%200.39879759519038077%2C%200.40080160320641284%2C%200.4028056112224449%2C%200.40480961923847697%2C%200.40681362725450904%2C%200.4088176352705411%2C%200.41082164328657317%2C%200.41282565130260523%2C%200.4148296593186373%2C%200.4168336673346693%2C%200.4188376753507014%2C%200.42084168336673344%2C%200.4228456913827655%2C%200.4248496993987976%2C%200.42685370741482964%2C%200.4288577154308617%2C%200.4308617234468938%2C%200.43286573146292584%2C%200.4348697394789579%2C%200.43687374749499%2C%200.43887775551102204%2C%200.4408817635270541%2C%200.44288577154308617%2C%200.44488977955911824%2C%200.4468937875751503%2C%200.44889779559118237%2C%200.45090180360721444%2C%200.4529058116232465%2C%200.45490981963927857%2C%200.45691382765531063%2C%200.4589178356713427%2C%200.46092184368737477%2C%200.46292585170340683%2C%200.4649298597194389%2C%200.46693386773547096%2C%200.46893787575150303%2C%200.4709418837675351%2C%200.4729458917835671%2C%200.4749498997995992%2C%200.47695390781563124%2C%200.4789579158316633%2C%200.48096192384769537%2C%200.48296593186372744%2C%200.4849699398797595%2C%200.48697394789579157%2C%200.48897795591182364%2C%200.4909819639278557%2C%200.49298597194388777%2C%200.49498997995991983%2C%200.4969939879759519%2C%200.49899799599198397%2C%200.501002004008016%2C%200.503006012024048%2C%200.5050100200400801%2C%200.5070140280561122%2C%200.5090180360721442%2C%200.5110220440881763%2C%200.5130260521042084%2C%200.5150300601202404%2C%200.5170340681362725%2C%200.5190380761523046%2C%200.5210420841683366%2C%200.5230460921843687%2C%200.5250501002004008%2C%200.5270541082164328%2C%200.5290581162324649%2C%200.531062124248497%2C%200.533066132264529%2C%200.5350701402805611%2C%200.5370741482965932%2C%200.5390781563126252%2C%200.5410821643286573%2C%200.5430861723446894%2C%200.5450901803607214%2C%200.5470941883767535%2C%200.5490981963927856%2C%200.5511022044088176%2C%200.5531062124248497%2C%200.5551102204408818%2C%200.5571142284569138%2C%200.5591182364729459%2C%200.561122244488978%2C%200.56312625250501%2C%200.5651302605210421%2C%200.5671342685370742%2C%200.5691382765531062%2C%200.5711422845691383%2C%200.5731462925851704%2C%200.5751503006012024%2C%200.5771543086172345%2C%200.5791583166332666%2C%200.5811623246492986%2C%200.5831663326653307%2C%200.5851703406813628%2C%200.5871743486973948%2C%200.5891783567134269%2C%200.591182364729459%2C%200.593186372745491%2C%200.5951903807615231%2C%200.5971943887775552%2C%200.5991983967935872%2C%200.6012024048096193%2C%200.6032064128256514%2C%200.6052104208416834%2C%200.6072144288577155%2C%200.6092184368737475%2C%200.6112224448897795%2C%200.6132264529058116%2C%200.6152304609218436%2C%200.6172344689378757%2C%200.6192384769539078%2C%200.6212424849699398%2C%200.6232464929859719%2C%200.625250501002004%2C%200.627254509018036%2C%200.6292585170340681%2C%200.6312625250501002%2C%200.6332665330661322%2C%200.6352705410821643%2C%200.6372745490981964%2C%200.6392785571142284%2C%200.6412825651302605%2C%200.6432865731462926%2C%200.6452905811623246%2C%200.6472945891783567%2C%200.6492985971943888%2C%200.6513026052104208%2C%200.6533066132264529%2C%200.655310621242485%2C%200.657314629258517%2C%200.6593186372745491%2C%200.6613226452905812%2C%200.6633266533066132%2C%200.6653306613226453%2C%200.6673346693386774%2C%200.6693386773547094%2C%200.6713426853707415%2C%200.6733466933867736%2C%200.6753507014028056%2C%200.6773547094188377%2C%200.6793587174348698%2C%200.6813627254509018%2C%200.6833667334669339%2C%200.685370741482966%2C%200.687374749498998%2C%200.6893787575150301%2C%200.6913827655310621%2C%200.6933867735470942%2C%200.6953907815631263%2C%200.6973947895791583%2C%200.6993987975951904%2C%200.7014028056112225%2C%200.7034068136272545%2C%200.7054108216432866%2C%200.7074148296593187%2C%200.7094188376753507%2C%200.7114228456913828%2C%200.7134268537074149%2C%200.7154308617234469%2C%200.717434869739479%2C%200.7194388777555111%2C%200.7214428857715431%2C%200.7234468937875751%2C%200.7254509018036072%2C%200.7274549098196392%2C%200.7294589178356713%2C%200.7314629258517034%2C%200.7334669338677354%2C%200.7354709418837675%2C%200.7374749498997996%2C%200.7394789579158316%2C%200.7414829659318637%2C%200.7434869739478958%2C%200.7454909819639278%2C%200.7474949899799599%2C%200.749498997995992%2C%200.751503006012024%2C%200.7535070140280561%2C%200.7555110220440882%2C%200.7575150300601202%2C%200.7595190380761523%2C%200.7615230460921844%2C%200.7635270541082164%2C%200.7655310621242485%2C%200.7675350701402806%2C%200.7695390781563126%2C%200.7715430861723447%2C%200.7735470941883767%2C%200.7755511022044088%2C%200.7775551102204409%2C%200.779559118236473%2C%200.781563126252505%2C%200.7835671342685371%2C%200.7855711422845691%2C%200.7875751503006012%2C%200.7895791583166333%2C%200.7915831663326653%2C%200.7935871743486974%2C%200.7955911823647295%2C%200.7975951903807615%2C%200.7995991983967936%2C%200.8016032064128257%2C%200.8036072144288577%2C%200.8056112224448898%2C%200.8076152304609219%2C%200.8096192384769539%2C%200.811623246492986%2C%200.8136272545090181%2C%200.8156312625250501%2C%200.8176352705410822%2C%200.8196392785571143%2C%200.8216432865731463%2C%200.8236472945891784%2C%200.8256513026052105%2C%200.8276553106212425%2C%200.8296593186372746%2C%200.8316633266533067%2C%200.8336673346693386%2C%200.8356713426853707%2C%200.8376753507014028%2C%200.8396793587174348%2C%200.8416833667334669%2C%200.843687374749499%2C%200.845691382765531%2C%200.8476953907815631%2C%200.8496993987975952%2C%200.8517034068136272%2C%200.8537074148296593%2C%200.8557114228456913%2C%200.8577154308617234%2C%200.8597194388777555%2C%200.8617234468937875%2C%200.8637274549098196%2C%200.8657314629258517%2C%200.8677354709418837%2C%200.8697394789579158%2C%200.8717434869739479%2C%200.87374749498998%2C%200.875751503006012%2C%200.8777555110220441%2C%200.8797595190380761%2C%200.8817635270541082%2C%200.8837675350701403%2C%200.8857715430861723%2C%200.8877755511022044%2C%200.8897795591182365%2C%200.8917835671342685%2C%200.8937875751503006%2C%200.8957915831663327%2C%200.8977955911823647%2C%200.8997995991983968%2C%200.9018036072144289%2C%200.9038076152304609%2C%200.905811623246493%2C%200.9078156312625251%2C%200.9098196392785571%2C%200.9118236472945892%2C%200.9138276553106213%2C%200.9158316633266533%2C%200.9178356713426854%2C%200.9198396793587175%2C%200.9218436873747495%2C%200.9238476953907816%2C%200.9258517034068137%2C%200.9278557114228457%2C%200.9298597194388778%2C%200.9318637274549099%2C%200.9338677354709419%2C%200.935871743486974%2C%200.9378757515030061%2C%200.9398797595190381%2C%200.9418837675350702%2C%200.9438877755511023%2C%200.9458917835671342%2C%200.9478957915831663%2C%200.9498997995991983%2C%200.9519038076152304%2C%200.9539078156312625%2C%200.9559118236472945%2C%200.9579158316633266%2C%200.9599198396793587%2C%200.9619238476953907%2C%200.9639278557114228%2C%200.9659318637274549%2C%200.9679358717434869%2C%200.969939879759519%2C%200.9719438877755511%2C%200.9739478957915831%2C%200.9759519038076152%2C%200.9779559118236473%2C%200.9799599198396793%2C%200.9819639278557114%2C%200.9839679358717435%2C%200.9859719438877755%2C%200.9879759519038076%2C%200.9899799599198397%2C%200.9919839679358717%2C%200.9939879759519038%2C%200.9959919839679359%2C%200.9979959919839679%2C%201.0%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20.range%28%5B%27%23add8e6ff%27%2C%20%27%23add8e6ff%27%2C%20%27%23aed7e5ff%27%2C%20%27%23aed7e5ff%27%2C%20%27%23aed7e5ff%27%2C%20%27%23aed6e4ff%27%2C%20%27%23aed6e4ff%27%2C%20%27%23aed5e3ff%27%2C%20%27%23aed5e3ff%27%2C%20%27%23afd4e2ff%27%2C%20%27%23afd4e2ff%27%2C%20%27%23afd4e1ff%27%2C%20%27%23afd3e1ff%27%2C%20%27%23afd3e0ff%27%2C%20%27%23afd2e0ff%27%2C%20%27%23b0d2dfff%27%2C%20%27%23b0d1dfff%27%2C%20%27%23b0d1dfff%27%2C%20%27%23b0d1deff%27%2C%20%27%23b0d0deff%27%2C%20%27%23b0d0ddff%27%2C%20%27%23b1cfddff%27%2C%20%27%23b1cfdcff%27%2C%20%27%23b1cedcff%27%2C%20%27%23b1cedbff%27%2C%20%27%23b1cddbff%27%2C%20%27%23b1cddaff%27%2C%20%27%23b2cddaff%27%2C%20%27%23b2ccd9ff%27%2C%20%27%23b2ccd9ff%27%2C%20%27%23b2cbd9ff%27%2C%20%27%23b2cbd8ff%27%2C%20%27%23b2cad8ff%27%2C%20%27%23b3cad7ff%27%2C%20%27%23b3cad7ff%27%2C%20%27%23b3c9d6ff%27%2C%20%27%23b3c9d6ff%27%2C%20%27%23b3c8d5ff%27%2C%20%27%23b3c8d5ff%27%2C%20%27%23b4c7d4ff%27%2C%20%27%23b4c7d4ff%27%2C%20%27%23b4c7d3ff%27%2C%20%27%23b4c6d3ff%27%2C%20%27%23b4c6d3ff%27%2C%20%27%23b4c5d2ff%27%2C%20%27%23b5c5d2ff%27%2C%20%27%23b5c4d1ff%27%2C%20%27%23b5c4d1ff%27%2C%20%27%23b5c3d0ff%27%2C%20%27%23b5c3d0ff%27%2C%20%27%23b5c3cfff%27%2C%20%27%23b6c2cfff%27%2C%20%27%23b6c2ceff%27%2C%20%27%23b6c1ceff%27%2C%20%27%23b6c1cdff%27%2C%20%27%23b6c0cdff%27%2C%20%27%23b6c0ccff%27%2C%20%27%23b7c0ccff%27%2C%20%27%23b7bfccff%27%2C%20%27%23b7bfcbff%27%2C%20%27%23b7becbff%27%2C%20%27%23b7becaff%27%2C%20%27%23b7bdcaff%27%2C%20%27%23b8bdc9ff%27%2C%20%27%23b8bdc9ff%27%2C%20%27%23b8bcc8ff%27%2C%20%27%23b8bcc8ff%27%2C%20%27%23b8bbc7ff%27%2C%20%27%23b8bbc7ff%27%2C%20%27%23b9bac6ff%27%2C%20%27%23b9bac6ff%27%2C%20%27%23b9b9c6ff%27%2C%20%27%23b9b9c5ff%27%2C%20%27%23b9b9c5ff%27%2C%20%27%23b9b8c4ff%27%2C%20%27%23bab8c4ff%27%2C%20%27%23bab7c3ff%27%2C%20%27%23bab7c3ff%27%2C%20%27%23bab6c2ff%27%2C%20%27%23bab6c2ff%27%2C%20%27%23bab6c1ff%27%2C%20%27%23bbb5c1ff%27%2C%20%27%23bbb5c0ff%27%2C%20%27%23bbb4c0ff%27%2C%20%27%23bbb4c0ff%27%2C%20%27%23bbb3bfff%27%2C%20%27%23bbb3bfff%27%2C%20%27%23bcb3beff%27%2C%20%27%23bcb2beff%27%2C%20%27%23bcb2bdff%27%2C%20%27%23bcb1bdff%27%2C%20%27%23bcb1bcff%27%2C%20%27%23bcb0bcff%27%2C%20%27%23bdb0bbff%27%2C%20%27%23bdafbbff%27%2C%20%27%23bdafbaff%27%2C%20%27%23bdafbaff%27%2C%20%27%23bdaebaff%27%2C%20%27%23bdaeb9ff%27%2C%20%27%23beadb9ff%27%2C%20%27%23beadb8ff%27%2C%20%27%23beacb8ff%27%2C%20%27%23beacb7ff%27%2C%20%27%23beacb7ff%27%2C%20%27%23beabb6ff%27%2C%20%27%23bfabb6ff%27%2C%20%27%23bfaab5ff%27%2C%20%27%23bfaab5ff%27%2C%20%27%23bfa9b4ff%27%2C%20%27%23bfa9b4ff%27%2C%20%27%23bfa9b4ff%27%2C%20%27%23bfa8b3ff%27%2C%20%27%23c0a8b3ff%27%2C%20%27%23c0a7b2ff%27%2C%20%27%23c0a7b2ff%27%2C%20%27%23c0a6b1ff%27%2C%20%27%23c0a6b1ff%27%2C%20%27%23c0a6b0ff%27%2C%20%27%23c1a5b0ff%27%2C%20%27%23c1a5afff%27%2C%20%27%23c1a4afff%27%2C%20%27%23c1a4aeff%27%2C%20%27%23c1a3aeff%27%2C%20%27%23c1a3adff%27%2C%20%27%23c2a2adff%27%2C%20%27%23c2a2adff%27%2C%20%27%23c2a2acff%27%2C%20%27%23c2a1acff%27%2C%20%27%23c2a1abff%27%2C%20%27%23c2a0abff%27%2C%20%27%23c3a0aaff%27%2C%20%27%23c39faaff%27%2C%20%27%23c39fa9ff%27%2C%20%27%23c39fa9ff%27%2C%20%27%23c39ea8ff%27%2C%20%27%23c39ea8ff%27%2C%20%27%23c49da7ff%27%2C%20%27%23c49da7ff%27%2C%20%27%23c49ca7ff%27%2C%20%27%23c49ca6ff%27%2C%20%27%23c49ca6ff%27%2C%20%27%23c49ba5ff%27%2C%20%27%23c59ba5ff%27%2C%20%27%23c59aa4ff%27%2C%20%27%23c59aa4ff%27%2C%20%27%23c599a3ff%27%2C%20%27%23c599a3ff%27%2C%20%27%23c598a2ff%27%2C%20%27%23c698a2ff%27%2C%20%27%23c698a1ff%27%2C%20%27%23c697a1ff%27%2C%20%27%23c697a1ff%27%2C%20%27%23c696a0ff%27%2C%20%27%23c696a0ff%27%2C%20%27%23c7959fff%27%2C%20%27%23c7959fff%27%2C%20%27%23c7959eff%27%2C%20%27%23c7949eff%27%2C%20%27%23c7949dff%27%2C%20%27%23c7939dff%27%2C%20%27%23c8939cff%27%2C%20%27%23c8929cff%27%2C%20%27%23c8929bff%27%2C%20%27%23c8929bff%27%2C%20%27%23c8919bff%27%2C%20%27%23c8919aff%27%2C%20%27%23c9909aff%27%2C%20%27%23c99099ff%27%2C%20%27%23c98f99ff%27%2C%20%27%23c98f98ff%27%2C%20%27%23c98e98ff%27%2C%20%27%23c98e97ff%27%2C%20%27%23ca8e97ff%27%2C%20%27%23ca8d96ff%27%2C%20%27%23ca8d96ff%27%2C%20%27%23ca8c95ff%27%2C%20%27%23ca8c95ff%27%2C%20%27%23ca8b94ff%27%2C%20%27%23cb8b94ff%27%2C%20%27%23cb8b94ff%27%2C%20%27%23cb8a93ff%27%2C%20%27%23cb8a93ff%27%2C%20%27%23cb8992ff%27%2C%20%27%23cb8992ff%27%2C%20%27%23cc8891ff%27%2C%20%27%23cc8891ff%27%2C%20%27%23cc8890ff%27%2C%20%27%23cc8790ff%27%2C%20%27%23cc878fff%27%2C%20%27%23cc868fff%27%2C%20%27%23cd868eff%27%2C%20%27%23cd858eff%27%2C%20%27%23cd858eff%27%2C%20%27%23cd848dff%27%2C%20%27%23cd848dff%27%2C%20%27%23cd848cff%27%2C%20%27%23ce838cff%27%2C%20%27%23ce838bff%27%2C%20%27%23ce828bff%27%2C%20%27%23ce828aff%27%2C%20%27%23ce818aff%27%2C%20%27%23ce8189ff%27%2C%20%27%23cf8189ff%27%2C%20%27%23cf8088ff%27%2C%20%27%23cf8088ff%27%2C%20%27%23cf7f88ff%27%2C%20%27%23cf7f87ff%27%2C%20%27%23cf7e87ff%27%2C%20%27%23cf7e86ff%27%2C%20%27%23d07e86ff%27%2C%20%27%23d07d85ff%27%2C%20%27%23d07d85ff%27%2C%20%27%23d07c84ff%27%2C%20%27%23d07c84ff%27%2C%20%27%23d07b83ff%27%2C%20%27%23d17b83ff%27%2C%20%27%23d17a82ff%27%2C%20%27%23d17a82ff%27%2C%20%27%23d17a82ff%27%2C%20%27%23d17981ff%27%2C%20%27%23d17981ff%27%2C%20%27%23d27880ff%27%2C%20%27%23d27880ff%27%2C%20%27%23d2777fff%27%2C%20%27%23d2777fff%27%2C%20%27%23d2777eff%27%2C%20%27%23d2767eff%27%2C%20%27%23d3767dff%27%2C%20%27%23d3757dff%27%2C%20%27%23d3757cff%27%2C%20%27%23d3747cff%27%2C%20%27%23d3747cff%27%2C%20%27%23d3747bff%27%2C%20%27%23d4737bff%27%2C%20%27%23d4737aff%27%2C%20%27%23d4727aff%27%2C%20%27%23d47279ff%27%2C%20%27%23d47179ff%27%2C%20%27%23d47178ff%27%2C%20%27%23d57078ff%27%2C%20%27%23d57077ff%27%2C%20%27%23d57077ff%27%2C%20%27%23d56f76ff%27%2C%20%27%23d56f76ff%27%2C%20%27%23d56e75ff%27%2C%20%27%23d66e75ff%27%2C%20%27%23d66d75ff%27%2C%20%27%23d66d74ff%27%2C%20%27%23d66d74ff%27%2C%20%27%23d66c73ff%27%2C%20%27%23d66c73ff%27%2C%20%27%23d76b72ff%27%2C%20%27%23d76b72ff%27%2C%20%27%23d76a71ff%27%2C%20%27%23d76a71ff%27%2C%20%27%23d76a70ff%27%2C%20%27%23d76970ff%27%2C%20%27%23d8696fff%27%2C%20%27%23d8686fff%27%2C%20%27%23d8686fff%27%2C%20%27%23d8676eff%27%2C%20%27%23d8676eff%27%2C%20%27%23d8666dff%27%2C%20%27%23d9666dff%27%2C%20%27%23d9666cff%27%2C%20%27%23d9656cff%27%2C%20%27%23d9656bff%27%2C%20%27%23d9646bff%27%2C%20%27%23d9646aff%27%2C%20%27%23da636aff%27%2C%20%27%23da6369ff%27%2C%20%27%23da6369ff%27%2C%20%27%23da6269ff%27%2C%20%27%23da6268ff%27%2C%20%27%23da6168ff%27%2C%20%27%23db6167ff%27%2C%20%27%23db6067ff%27%2C%20%27%23db6066ff%27%2C%20%27%23db6066ff%27%2C%20%27%23db5f65ff%27%2C%20%27%23db5f65ff%27%2C%20%27%23dc5e64ff%27%2C%20%27%23dc5e64ff%27%2C%20%27%23dc5d63ff%27%2C%20%27%23dc5d63ff%27%2C%20%27%23dc5c63ff%27%2C%20%27%23dc5c62ff%27%2C%20%27%23dd5c62ff%27%2C%20%27%23dd5b61ff%27%2C%20%27%23dd5b61ff%27%2C%20%27%23dd5a60ff%27%2C%20%27%23dd5a60ff%27%2C%20%27%23dd595fff%27%2C%20%27%23de595fff%27%2C%20%27%23de595eff%27%2C%20%27%23de585eff%27%2C%20%27%23de585dff%27%2C%20%27%23de575dff%27%2C%20%27%23de575dff%27%2C%20%27%23df565cff%27%2C%20%27%23df565cff%27%2C%20%27%23df565bff%27%2C%20%27%23df555bff%27%2C%20%27%23df555aff%27%2C%20%27%23df545aff%27%2C%20%27%23df5459ff%27%2C%20%27%23e05359ff%27%2C%20%27%23e05358ff%27%2C%20%27%23e05358ff%27%2C%20%27%23e05257ff%27%2C%20%27%23e05257ff%27%2C%20%27%23e05156ff%27%2C%20%27%23e15156ff%27%2C%20%27%23e15056ff%27%2C%20%27%23e15055ff%27%2C%20%27%23e14f55ff%27%2C%20%27%23e14f54ff%27%2C%20%27%23e14f54ff%27%2C%20%27%23e24e53ff%27%2C%20%27%23e24e53ff%27%2C%20%27%23e24d52ff%27%2C%20%27%23e24d52ff%27%2C%20%27%23e24c51ff%27%2C%20%27%23e24c51ff%27%2C%20%27%23e34c50ff%27%2C%20%27%23e34b50ff%27%2C%20%27%23e34b50ff%27%2C%20%27%23e34a4fff%27%2C%20%27%23e34a4fff%27%2C%20%27%23e3494eff%27%2C%20%27%23e4494eff%27%2C%20%27%23e4494dff%27%2C%20%27%23e4484dff%27%2C%20%27%23e4484cff%27%2C%20%27%23e4474cff%27%2C%20%27%23e4474bff%27%2C%20%27%23e5464bff%27%2C%20%27%23e5464aff%27%2C%20%27%23e5454aff%27%2C%20%27%23e5454aff%27%2C%20%27%23e54549ff%27%2C%20%27%23e54449ff%27%2C%20%27%23e64448ff%27%2C%20%27%23e64348ff%27%2C%20%27%23e64347ff%27%2C%20%27%23e64247ff%27%2C%20%27%23e64246ff%27%2C%20%27%23e64246ff%27%2C%20%27%23e74145ff%27%2C%20%27%23e74145ff%27%2C%20%27%23e74044ff%27%2C%20%27%23e74044ff%27%2C%20%27%23e73f44ff%27%2C%20%27%23e73f43ff%27%2C%20%27%23e83f43ff%27%2C%20%27%23e83e42ff%27%2C%20%27%23e83e42ff%27%2C%20%27%23e83d41ff%27%2C%20%27%23e83d41ff%27%2C%20%27%23e83c40ff%27%2C%20%27%23e93c40ff%27%2C%20%27%23e93b3fff%27%2C%20%27%23e93b3fff%27%2C%20%27%23e93b3eff%27%2C%20%27%23e93a3eff%27%2C%20%27%23e93a3eff%27%2C%20%27%23ea393dff%27%2C%20%27%23ea393dff%27%2C%20%27%23ea383cff%27%2C%20%27%23ea383cff%27%2C%20%27%23ea383bff%27%2C%20%27%23ea373bff%27%2C%20%27%23eb373aff%27%2C%20%27%23eb363aff%27%2C%20%27%23eb3639ff%27%2C%20%27%23eb3539ff%27%2C%20%27%23eb3538ff%27%2C%20%27%23eb3538ff%27%2C%20%27%23ec3437ff%27%2C%20%27%23ec3437ff%27%2C%20%27%23ec3337ff%27%2C%20%27%23ec3336ff%27%2C%20%27%23ec3236ff%27%2C%20%27%23ec3235ff%27%2C%20%27%23ed3135ff%27%2C%20%27%23ed3134ff%27%2C%20%27%23ed3134ff%27%2C%20%27%23ed3033ff%27%2C%20%27%23ed3033ff%27%2C%20%27%23ed2f32ff%27%2C%20%27%23ee2f32ff%27%2C%20%27%23ee2e31ff%27%2C%20%27%23ee2e31ff%27%2C%20%27%23ee2e31ff%27%2C%20%27%23ee2d30ff%27%2C%20%27%23ee2d30ff%27%2C%20%27%23ef2c2fff%27%2C%20%27%23ef2c2fff%27%2C%20%27%23ef2b2eff%27%2C%20%27%23ef2b2eff%27%2C%20%27%23ef2b2dff%27%2C%20%27%23ef2a2dff%27%2C%20%27%23ef2a2cff%27%2C%20%27%23f0292cff%27%2C%20%27%23f0292bff%27%2C%20%27%23f0282bff%27%2C%20%27%23f0282bff%27%2C%20%27%23f0272aff%27%2C%20%27%23f0272aff%27%2C%20%27%23f12729ff%27%2C%20%27%23f12629ff%27%2C%20%27%23f12628ff%27%2C%20%27%23f12528ff%27%2C%20%27%23f12527ff%27%2C%20%27%23f12427ff%27%2C%20%27%23f22426ff%27%2C%20%27%23f22426ff%27%2C%20%27%23f22325ff%27%2C%20%27%23f22325ff%27%2C%20%27%23f22225ff%27%2C%20%27%23f22224ff%27%2C%20%27%23f32124ff%27%2C%20%27%23f32123ff%27%2C%20%27%23f32123ff%27%2C%20%27%23f32022ff%27%2C%20%27%23f32022ff%27%2C%20%27%23f31f21ff%27%2C%20%27%23f41f21ff%27%2C%20%27%23f41e20ff%27%2C%20%27%23f41e20ff%27%2C%20%27%23f41d1fff%27%2C%20%27%23f41d1fff%27%2C%20%27%23f41d1fff%27%2C%20%27%23f51c1eff%27%2C%20%27%23f51c1eff%27%2C%20%27%23f51b1dff%27%2C%20%27%23f51b1dff%27%2C%20%27%23f51a1cff%27%2C%20%27%23f51a1cff%27%2C%20%27%23f61a1bff%27%2C%20%27%23f6191bff%27%2C%20%27%23f6191aff%27%2C%20%27%23f6181aff%27%2C%20%27%23f61819ff%27%2C%20%27%23f61719ff%27%2C%20%27%23f71718ff%27%2C%20%27%23f71718ff%27%2C%20%27%23f71618ff%27%2C%20%27%23f71617ff%27%2C%20%27%23f71517ff%27%2C%20%27%23f71516ff%27%2C%20%27%23f81416ff%27%2C%20%27%23f81415ff%27%2C%20%27%23f81315ff%27%2C%20%27%23f81314ff%27%2C%20%27%23f81314ff%27%2C%20%27%23f81213ff%27%2C%20%27%23f91213ff%27%2C%20%27%23f91112ff%27%2C%20%27%23f91112ff%27%2C%20%27%23f91012ff%27%2C%20%27%23f91011ff%27%2C%20%27%23f91011ff%27%2C%20%27%23fa0f10ff%27%2C%20%27%23fa0f10ff%27%2C%20%27%23fa0e0fff%27%2C%20%27%23fa0e0fff%27%2C%20%27%23fa0d0eff%27%2C%20%27%23fa0d0eff%27%2C%20%27%23fb0d0dff%27%2C%20%27%23fb0c0dff%27%2C%20%27%23fb0c0cff%27%2C%20%27%23fb0b0cff%27%2C%20%27%23fb0b0cff%27%2C%20%27%23fb0a0bff%27%2C%20%27%23fc0a0bff%27%2C%20%27%23fc090aff%27%2C%20%27%23fc090aff%27%2C%20%27%23fc0909ff%27%2C%20%27%23fc0809ff%27%2C%20%27%23fc0808ff%27%2C%20%27%23fd0708ff%27%2C%20%27%23fd0707ff%27%2C%20%27%23fd0607ff%27%2C%20%27%23fd0606ff%27%2C%20%27%23fd0606ff%27%2C%20%27%23fd0506ff%27%2C%20%27%23fe0505ff%27%2C%20%27%23fe0405ff%27%2C%20%27%23fe0404ff%27%2C%20%27%23fe0304ff%27%2C%20%27%23fe0303ff%27%2C%20%27%23fe0303ff%27%2C%20%27%23ff0202ff%27%2C%20%27%23ff0202ff%27%2C%20%27%23ff0101ff%27%2C%20%27%23ff0101ff%27%2C%20%27%23ff0000ff%27%2C%20%27%23ff0000ff%27%2C%20%27%23ff0000ff%27%5D%29%3B%0A%20%20%20%20%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.x%20%3D%20d3.scale.linear%28%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20.domain%28%5B0.0%2C%201.0%5D%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20.range%28%5B0%2C%20400%5D%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.legend%20%3D%20L.control%28%7Bposition%3A%20%27topright%27%7D%29%3B%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.legend.onAdd%20%3D%20function%20%28map%29%20%7Bvar%20div%20%3D%20L.DomUtil.create%28%27div%27%2C%20%27legend%27%29%3B%20return%20div%7D%3B%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.legend.addTo%28map_557bdd16e7d346f8b4b957e7aed05ba4%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.xAxis%20%3D%20d3.svg.axis%28%29%0A%20%20%20%20%20%20%20%20.scale%28color_map_0c76c012401b474fb893e7b3e7412d90.x%29%0A%20%20%20%20%20%20%20%20.orient%28%22top%22%29%0A%20%20%20%20%20%20%20%20.tickSize%281%29%0A%20%20%20%20%20%20%20%20.tickValues%28%5B0%2C%201%5D%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.svg%20%3D%20d3.select%28%22.legend.leaflet-control%22%29.append%28%22svg%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22id%22%2C%20%27legend%27%29%0A%20%20%20%20%20%20%20%20.attr%28%22width%22%2C%20450%29%0A%20%20%20%20%20%20%20%20.attr%28%22height%22%2C%2040%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.g%20%3D%20color_map_0c76c012401b474fb893e7b3e7412d90.svg.append%28%22g%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22class%22%2C%20%22key%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22transform%22%2C%20%22translate%2825%2C16%29%22%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.g.selectAll%28%22rect%22%29%0A%20%20%20%20%20%20%20%20.data%28color_map_0c76c012401b474fb893e7b3e7412d90.color.range%28%29.map%28function%28d%2C%20i%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20x0%3A%20i%20%3F%20color_map_0c76c012401b474fb893e7b3e7412d90.x%28color_map_0c76c012401b474fb893e7b3e7412d90.color.domain%28%29%5Bi%20-%201%5D%29%20%3A%20color_map_0c76c012401b474fb893e7b3e7412d90.x.range%28%29%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x1%3A%20i%20%3C%20color_map_0c76c012401b474fb893e7b3e7412d90.color.domain%28%29.length%20%3F%20color_map_0c76c012401b474fb893e7b3e7412d90.x%28color_map_0c76c012401b474fb893e7b3e7412d90.color.domain%28%29%5Bi%5D%29%20%3A%20color_map_0c76c012401b474fb893e7b3e7412d90.x.range%28%29%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20z%3A%20d%0A%20%20%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20%7D%29%29%0A%20%20%20%20%20%20.enter%28%29.append%28%22rect%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22height%22%2C%2010%29%0A%20%20%20%20%20%20%20%20.attr%28%22x%22%2C%20function%28d%29%20%7B%20return%20d.x0%3B%20%7D%29%0A%20%20%20%20%20%20%20%20.attr%28%22width%22%2C%20function%28d%29%20%7B%20return%20d.x1%20-%20d.x0%3B%20%7D%29%0A%20%20%20%20%20%20%20%20.style%28%22fill%22%2C%20function%28d%29%20%7B%20return%20d.z%3B%20%7D%29%3B%0A%0A%20%20%20%20color_map_0c76c012401b474fb893e7b3e7412d90.g.call%28color_map_0c76c012401b474fb893e7b3e7412d90.xAxis%29.append%28%22text%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22class%22%2C%20%22caption%22%29%0A%20%20%20%20%20%20%20%20.attr%28%22y%22%2C%2021%29%0A%20%20%20%20%20%20%20%20.text%28%27%27%29%3B%0A%3C/script%3E onload=\"this.contentDocument.open();this.contentDocument.write(    decodeURIComponent(this.getAttribute('data-html')));this.contentDocument.close();\" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe></div></div>"
      ],
      "text/plain": [
       "<folium.folium.Map at 0x7f9fb185ed10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import folium\n",
    "import folium.plugins as folium_plugins\n",
    "import branca.colormap as cm\n",
    "\n",
    "colormap = cm.LinearColormap(colors=[\"lightblue\", \"red\"], index=[0, 1], vmin=0, vmax=1)\n",
    "map = folium.Map(\n",
    "    location=[43.44, -7.86],\n",
    "    zoom_start=16,\n",
    "    tiles=\"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}\",\n",
    "    attr=\"ESRI\",\n",
    ")\n",
    "for loc, p in zip(zip(lats, lons), plant_predictions):\n",
    "    folium.Circle(\n",
    "        location=loc,\n",
    "        radius=20,\n",
    "        fill=True,\n",
    "        color=colormap(p),\n",
    "    ).add_to(map)\n",
    "\n",
    "map.add_child(colormap)\n",
    "\n",
    "display(map)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efcabd53-e81e-4d72-a453-e2fb6dcaa7ae",
   "metadata": {
    "id": "efcabd53-e81e-4d72-a453-e2fb6dcaa7ae"
   },
   "source": [
    "# 6. 🧹 Clean Up"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b002e48a-035c-4369-a161-aa2d800a70b2",
   "metadata": {
    "id": "b002e48a-035c-4369-a161-aa2d800a70b2"
   },
   "source": [
    "To **avoid incurring charges** to your Google Cloud account for the resources used in this tutorial, either delete the project that contains the resources, or keep the project and delete the individual resources."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fc97850-80dd-4c24-80d0-f93825d96e0e",
   "metadata": {
    "id": "9fc97850-80dd-4c24-80d0-f93825d96e0e"
   },
   "source": [
    "## Deleting the project\n",
    "\n",
    "The **easiest** way to **eliminate billing** is to delete the project that you created for the tutorial.\n",
    "\n",
    "To delete the project:\n",
    "\n",
    "> ⚠️ Deleting a project has the following effects:\n",
    ">\n",
    "> * **Everything in the project is deleted.** If you used an existing project for this tutorial, when you delete it, you also delete any other work you've done in the project.\n",
    ">\n",
    "> * **Custom project IDs are lost.** When you created this project, you might have created a custom project ID that you want to use in the future. To preserve the URLs that use the project ID, such as an appspot.com URL, delete selected resources inside the project instead of deleting the whole project.\n",
    ">\n",
    "> If you plan to explore multiple tutorials and quickstarts, **reusing** projects can help you avoid exceeding project **quota limits**.\n",
    "\n",
    "1. In the Cloud Console, go to the **Manage resources** page.\n",
    "\n",
    "  <button>\n",
    "\n",
    "  [Go to Manage resources](https://console.cloud.google.com/iam-admin/projects)\n",
    "\n",
    "  </button>\n",
    "\n",
    "1. In the project list, select the project that you want to delete, and then click **Delete**.\n",
    "\n",
    "1. In the dialog, type the project ID, and then click **Shut down** to delete the project."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "README.ipynb",
   "provenance": [],
   "toc_visible": true
  },
  "environment": {
   "kernel": "python3",
   "name": "tf2-gpu.2-6.m82",
   "type": "gcloud",
   "uri": "gcr.io/deeplearning-platform-release/tf2-gpu.2-6:m82"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
